package ar.edu.utn.frba.dds.tp.tests.adapter;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.joda.time.LocalDate;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import ar.edu.utn.frba.dds.tp.code.Asientos.AsientoDeVuelo;
import ar.edu.utn.frba.dds.tp.code.Asientos.EstadoAsiento;
import ar.edu.utn.frba.dds.tp.code.adapter.OceanicAdapter;
import ar.edu.utn.frba.dds.tp.code.lugares.Lugar;
import ar.edu.utn.frba.dds.tp.code.lugares.NullLugar;
import ar.edu.utn.frba.dds.tp.code.usuarios.UsuarioStandard;
import ar.edu.utn.frba.dds.tp.code.usuarios.UsuarioVIP;
import ar.edu.utn.frba.dds.tp.code.vuelos.BuscadorVuelos;
import ar.edu.utn.frba.dds.tp.code.vuelos.Busqueda;
import ar.edu.utn.frba.dds.tp.code.vuelos.ExistingReservationException;
import ar.edu.utn.frba.dds.tp.code.vuelos.RegistroCompraAsientos;
import ar.edu.utn.frba.dds.tp.code.vuelos.Vuelo;

import com.oceanic.AerolineaOceanic;
import com.oceanic.AsientoDTO;

public class OceanicTest {

	private OceanicAdapter oceanic = new OceanicAdapter();
	private Lugar origen = new Lugar("BA_");
	private Lugar destino = new Lugar("SLA");
	private Busqueda busquedaConDestino;
	private Busqueda busquedaSinDestino;
	private AsientoDeVuelo asiento;
	
	@Before
	public void initializeEnvironment() {
		BuscadorVuelos.getInstance().sacarAerolinea(new OceanicAdapter());
		
		Field field;
		try {
			field = OceanicAdapter.class.getDeclaredField("aerolineaOceanic");
			field.setAccessible(true);
			field.set(oceanic, new AerolineaOceanicMock());
		} catch (Exception e) {
			//no tengo que hacer nada.
		}
		BuscadorVuelos.getInstance().clearAerolineas();
		BuscadorVuelos.getInstance().agregarAerolinea(oceanic);
	
		busquedaConDestino = new Busqueda(origen, destino, new LocalDate(2012,8,15));
		busquedaSinDestino = new Busqueda(origen, new NullLugar(), new LocalDate(2012,8,15));
		asiento = oceanic.vuelosDisponibles(busquedaSinDestino).get(0).getAsientos().get(0);
	}
	
	//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	//+++ Adapter Test +++++++++++++++++++++++++++++++++++++++++++
	//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	
	@Test
	public void testBuscaVuelosConDestinoYEncuentraDos(){
		List<Vuelo> vuelosDisponibles = oceanic.vuelosDisponibles(busquedaConDestino);
		assertEquals(2, vuelosDisponibles.size());
	}
		
	@Test
	public void testBuscaVuelosSinDestinoYEncuentraTres(){
		List<Vuelo> vuelosDisponibles = oceanic.vuelosDisponibles(busquedaSinDestino);
		assertEquals(3, vuelosDisponibles.size());
	}
	
	@Test
	public void testComprarAsientoCambiaElEstado(){
		assertEquals(EstadoAsiento.DISPONIBLE, asiento.getEstado());
		oceanic.comprarAsiento(asiento, new UsuarioVIP());
		assertEquals(EstadoAsiento.COMPRADO,asiento.getEstado());
	}
	
	//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	//+++ Reservas Test ++++++++++++++++++++++++++++++++++++++++++
	//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	
	@Test 
	public void testReservarAsiento(){
		oceanic.reservarAsiento(asiento, new UsuarioStandard()); // solo los usuarios standard pueden reservar
		assertEquals(asiento.getEstado(), EstadoAsiento.RESERVADO); //si el estado cambio a reservado es porque oceanic reservo el asiento
	}
	
	@Test
	public void testAgregaSobreReservas(){
		UsuarioStandard juan = new UsuarioStandard();
		oceanic.reservarAsiento(asiento, new UsuarioStandard());
		assertTrue(asiento.getSobreReservas().isEmpty()); //no hay sobre-reservas ya que el asiento estaba disponible y se pudo reservar correctamente
		try {
			oceanic.reservarAsiento(asiento, juan);
		}
		catch (ExistingReservationException e){
			asiento.agregarSobreReserva(juan);
		}
		assertTrue(asiento.getSobreReservas().contains(juan));
		// el asiento contiene una sobre-reserva de juan (quiso reservar un asiento ya reservado) por eso salta excepcion
	}
	
	@Test
	public void testComprarEliminaSobreReservantes(){
		UsuarioVIP reservante = new UsuarioVIP();
		oceanic.reservarAsiento(asiento, reservante);
		try{
			oceanic.reservarAsiento(asiento, new UsuarioStandard());
		}
		catch(ExistingReservationException e){
			asiento.agregarSobreReserva( new UsuarioStandard());
		}
		
		assertFalse(asiento.getSobreReservas().isEmpty());
		oceanic.comprarAsiento(asiento, reservante);
		assertTrue(asiento.getSobreReservas().isEmpty());
		assertEquals(asiento.getEstado(),EstadoAsiento.COMPRADO);
	}
	
}


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++ Definición del mock de oceanic +++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


final class AerolineaOceanicMock extends AerolineaOceanic {
	private boolean primeraReserva = true;
	
	public List<AsientoDTO> asientosDisponiblesParaOrigen(String origen, String fechaDeSalida){
		List<AsientoDTO> asientos = new ArrayList<AsientoDTO>();
		asientos.add(new AsientoDTO("OC100",10,"15/08/2012","17/08/2012","10:35","05:35",new BigDecimal("3150.98"),"Ejecutiva","Pasillo",false,"BS_","SLA"));
		asientos.add(new AsientoDTO("OC100",11,"15/08/2012","17/08/2012","10:35","05:35",new BigDecimal("3150.98"),"Ejecutiva","Pasillo",false,"BS_","SLA"));
		asientos.add(new AsientoDTO("OC101",8,"15/08/2012","17/08/2012","10:35","05:35",new BigDecimal("3150.98"),"Ejecutiva","Pasillo",false,"BS_","SLA"));
		asientos.add(new AsientoDTO("OC111",22,"15/08/2012","17/08/2012","10:35","05:35",new BigDecimal("3150.98"),"Ejecutiva","Pasillo",false,"BS_","NY_"));
		return asientos;
	}
	
	public List<AsientoDTO> asientosDisponiblesParaOrigenYDestino(String origen, String destino, String fechaDeSalida) {
		List<AsientoDTO> asientos = new ArrayList<AsientoDTO>();
		asientos.add(new AsientoDTO("OC100",10,"15/08/2012","17/08/2012","10:35","05:35",new BigDecimal("3150.98"),"Ejecutiva","Pasillo",false,"BS_","SLA"));
		asientos.add(new AsientoDTO("OC100",11,"15/08/2012","17/08/2012","10:35","05:35",new BigDecimal("3150.98"),"Ejecutiva","Pasillo",false,"BS_","SLA"));
		asientos.add(new AsientoDTO("OC101",8,"15/08/2012","17/08/2012","10:35","05:35",new BigDecimal("3150.98"),"Ejecutiva","Pasillo",false,"BS_","SLA"));
		return asientos;
	}
	
	public Boolean comprarSiHayDisponibilidad(String dni, String codigoVuelo,Integer numeroDeAsiento) {
		return true;
	}

	public Boolean estaReservado(String codigoDeVuelo, Integer numeroDeAsiento) {
		return !primeraReserva;
	}
	
	public Boolean reservar(String dni, String codigoDeVuelo, Integer numeroDeAsiento) {
		if (primeraReserva){
			primeraReserva = false;
			return !primeraReserva;
		}
			return false;
	}
	
	@After
	public void tearDown(){
		RegistroCompraAsientos.getInstance().clear();
	}
}